package com.gameone.test.tree.data;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionException;
import javax.xml.xpath.XPathFunctionResolver;

import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Hello world!
 * 
 */
public class Xml {
	static class MyNamespaceContext implements NamespaceContext {
		public String getNamespaceURI(String prefix) {
			if (prefix == null)
				throw new NullPointerException("Null prefix");
			else if ("pp".equals(prefix))
				return "http://www.example.com/books";
			else if ("xml".equals(prefix))
				return XMLConstants.XML_NS_URI;
			return XMLConstants.NULL_NS_URI;
		}

		public String getPrefix(String namespaceURI) {
			if (namespaceURI.equals("http://www.example.com/books")) {
				return "pp";
			} else {
				return null;
			}
		}

		@SuppressWarnings("rawtypes")
		public Iterator getPrefixes(String namespaceURI) {
			throw new UnsupportedOperationException();
		}
	}

	static class MyFunction implements XPathFunction {
		@SuppressWarnings("rawtypes")
		public Object evaluate(List args) throws XPathFunctionException {
			if (args.size() != 1) {
				throw new XPathFunctionException("Wrong number of arguments");
			}

			Object o = args.get(0);

			return Integer.valueOf(((NodeList) o).item(0).getTextContent()) > 2003;
		}
	}

	static class MyFunctionResolver implements XPathFunctionResolver {
		private static final QName name = new QName("http://www.example.com/books", "valid-year");

		public XPathFunction resolveFunction(QName functionName, int arity) {
			if (functionName.equals(name) && arity == 1) {
				return new MyFunction();
			}
			return null;
		}
	}

	public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException {
		Date start = new Date();
		Long startM = Runtime.getRuntime().freeMemory();

		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse(Xml.class.getClassLoader().getResourceAsStream("empty.xml"));
		Element root = doc.getDocumentElement();

		BufferedReader reader = new BufferedReader(new InputStreamReader(Xml.class.getClassLoader()
				.getResourceAsStream("books.xml")));
		String line;
		while ((line = reader.readLine()) != null) {
			Node n = root.cloneNode(false);
			n.setTextContent(line);
			root.appendChild(n);
		}

		System.out.println("parse xml to doc use " + (new Date().getTime() - start.getTime()) + "ms");
		System.out.println("parse xml to doc use "
				+ (new DecimalFormat().format(Runtime.getRuntime().freeMemory() - startM)) + "Bytes");

		NodeList nodes = root.getChildNodes();
		// for (int i = 0; i < nodes.getLength(); i++) {
		// System.out.println(nodes.item(i).getTextContent());
		// }

	}

	@Test
	public void testLoad() throws DocumentException {
		Format format = new DecimalFormat();

		Long start = new Date().getTime();
		Long startM = Runtime.getRuntime().freeMemory();

		org.dom4j.Document doc = new SAXReader().read("D:/Work/projects/workspace/tree/src/books.xml");

		System.out.println(doc.selectNodes("/bookstore/book/year").size());

		Long end = new Date().getTime();
		Long endM = Runtime.getRuntime().freeMemory();

		System.out.println("SAXReader parse xml to doc use " + format.format(end - start) + "ms. and use "
				+ format.format(endM - startM) + "Bytes");
	}

	@Test
	public void testLoad2() throws DocumentException, ParserConfigurationException, SAXException, IOException {
		Format format = new DecimalFormat();

		Long start = new Date().getTime();
		Long startM = Runtime.getRuntime().freeMemory();

		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse("D:/Work/projects/workspace/tree/src/books.xml");

		Long end = new Date().getTime();
		Long endM = Runtime.getRuntime().freeMemory();

		System.out.println("parse xml to doc use " + format.format(end - start) + "ms. and use "
				+ format.format(endM - startM) + "Bytes");
	}

	public static void main2(String[] args) throws ParserConfigurationException, SAXException, IOException,
			XPathExpressionException {
		Long start = new Date().getTime();
		Long startM = Runtime.getRuntime().freeMemory();
		Format format = new DecimalFormat();

		DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
		domFactory.setNamespaceAware(true);
		DocumentBuilder builder = domFactory.newDocumentBuilder();
		Document doc = builder.parse("D:/Work/projects/workspace/tree/src/books.xml");

		Long toDoc = new Date().getTime();
		Long toDocM = Runtime.getRuntime().freeMemory();

		System.out.println("parse xml to doc use " + format.format(toDoc - start) + "ms. and use "
				+ format.format(toDocM - startM) + "Bytes");

		XPathFactory pathFactory = XPathFactory.newInstance();
		XPath xpath = pathFactory.newXPath();
		xpath.setNamespaceContext(new MyNamespaceContext());
		xpath.setXPathFunctionResolver(new MyFunctionResolver());

		XPathExpression expr = xpath.compile("/pp:bookstore/pp:book/pp:year/text()");

		Object result = expr.evaluate(doc, XPathConstants.NODESET);

		Long eval = new Date().getTime();
		Long evalM = Runtime.getRuntime().freeMemory();

		System.out.println("evaluate expr use " + format.format(eval - toDoc) + "ms. and use "
				+ format.format(evalM - toDocM) + "Bytes");
		// NodeList nodes = (NodeList) result;
		// System.out.println(nodes.getLength());
		//
		// for (int i = 0; i < nodes.getLength(); i++) {
		// System.out.println(nodes.item(i).getNodeValue());
		// }

	}
}
